package com.cfpamf.ms.insur.pay.service;

import com.alibaba.fastjson.JSON;
import com.cfpamf.ms.insur.pay.constant.SyncNotifyStatusEnum;
import com.cfpamf.ms.insur.pay.dao.PayOrderMapper;
import com.cfpamf.ms.insur.pay.dao.PayOrderTrxMapper;
import com.cfpamf.ms.insur.pay.dao.PayRefundMapper;
import com.cfpamf.ms.insur.pay.facade.constant.PayTypeEnum;
import com.cfpamf.ms.insur.pay.facade.dto.PayOrderNotifyDTO;
import com.cfpamf.ms.insur.pay.integration.BankPayService;
import com.cfpamf.ms.insur.pay.integration.dto.BankPayCheckNotifyRetDTO;
import com.cfpamf.ms.insur.pay.integration.dto.BankRefundCheckNotifyRetDTO;
import com.cfpamf.ms.insur.pay.pojo.convert.PayOrderConvert;
import com.cfpamf.ms.insur.pay.pojo.po.PayOrderPO;
import com.cfpamf.ms.insur.pay.pojo.po.PayOrderTrxPO;
import com.cfpamf.ms.insur.pay.pojo.po.PayRefundPO;
import com.cfpamf.ms.insur.pay.util.InsurUtil;
import com.cfpamf.ms.lock.annotation.EnableLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

/**
 * 保险第三方回调service
 */
@Slf4j
@Service
public class InsurNotifyService {

    @Autowired
    private InsurPayService insurPayService;

    @Autowired
    private PayOrderMapper payOrderMapper;

    @Autowired
    private PayOrderTrxMapper payOrderTrxMapper;

    @Autowired
    private PayRefundMapper payRefundMapper;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 支付宝支付成功异步回掉
     *
     * @param sourceSys
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String aliPayCallbackNotify(String sourceSys, HttpServletRequest request) {
        return commonPayCallBackNotify(sourceSys, PayTypeEnum.ALIPAY, request);
    }

    /**
     * 微信支付成功异步回掉接口
     *
     * @param sourceSys
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String wxPayCallbackNotify(String sourceSys, HttpServletRequest request) {
        return commonPayCallBackNotify(sourceSys, PayTypeEnum.WXPAY, request);
    }

    /**
     * 微信退款成功异步回掉接口
     *
     * @param sourceSys
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String wxRefundCallbackNotify(String sourceSys, HttpServletRequest request) {
        return commonRefundCallbackNotify(sourceSys, PayTypeEnum.WXPAY, request);
    }

    /**
     * 异步通知业务系统支付成功
     *
     * @param orderNo
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    @EnableLock(name = "insurance:pay:syncNotify", keys = {"#orderNo"})
    public void sourceSysPayCallbackNotify(String orderNo) {
        PayOrderPO queryCnt = new PayOrderPO();
        queryCnt.setOrderNo(orderNo);
        PayOrderPO entity = payOrderMapper.selectOne(queryCnt);
        if (!Objects.equals(entity.getSyncNoticeStatus(), SyncNotifyStatusEnum.PAY_NOTIFY_WAITING.getStatusCode())) {
            log.warn("【异步通知业务系统{}】 sourceOrderId={} 已经通知无需再次通知", entity.getSourceSys(), entity.getSourceOrderId());
            return;
        }
        String sourceSysNotifyUrl = entity.getNotifyUrl();
        if (StringUtils.isBlank(sourceSysNotifyUrl)) {
            log.error("【异步通知业务系统{}】 sourceOrderId={} 异步通知sourceSysNotifyUrl=NULL", entity.getSourceSys(), entity.getSourceOrderId());
            return;
        }
        PayOrderNotifyDTO notifyDTO = PayOrderConvert.CONVERT.payOrderPO2PayOrderNotifyDTO(entity);
        SyncNotifyStatusEnum syncNoticeStatus = null;
        try {
            log.info("【异步通知业务系统{}】start===== sourceOrderId={},sourceSysNotifyUrl={},param={}", notifyDTO.getSourceSys(), entity.getSourceOrderId(), sourceSysNotifyUrl, JSON.toJSONString(notifyDTO));
            ResponseEntity<String> respEntity = restTemplate.postForEntity(sourceSysNotifyUrl, notifyDTO, String.class);
            // 业务系统返回200 就为回调成功
            if (respEntity.getStatusCode() == HttpStatus.OK) {
                syncNoticeStatus = SyncNotifyStatusEnum.PAY_NOTIFY_SUCCESS;
            } else {
                syncNoticeStatus = SyncNotifyStatusEnum.PAY_NOTIFY_FAIL;
            }
        } catch (Exception e) {
            log.warn("", e);
            syncNoticeStatus = SyncNotifyStatusEnum.PAY_NOTIFY_FAIL;
        }
        log.info("【异步通知业务系统{}】end====== sourceOrderId={},sourceSysNotifyUrl={},status={}", notifyDTO.getSourceSys(), entity.getSourceOrderId(), sourceSysNotifyUrl, syncNoticeStatus.getStatusName());
        payOrderMapper.updatePayOrderSyncNoticeStatus(orderNo, syncNoticeStatus.getStatusCode());
    }

    /**
     * 支付成功异步共同处理
     *
     * @param sourceSys
     * @param payType
     * @param request
     * @return
     */
    private String commonPayCallBackNotify(String sourceSys, PayTypeEnum payType, HttpServletRequest request) {
        BankPayService bankPayService = InsurUtil.getBankPayService(payType.getTypeCode());
        BankPayCheckNotifyRetDTO retDTO = bankPayService.bankCheckPayNotify(sourceSys, request);
        if (retDTO.isPaySuccess()) {
            insurPayService.syncPayOrderPayInfoWithBank(sourceSys, retDTO.getPayTrxNo(), payType.getTypeCode());
            payOrderTrxMapper.updatePayOrderTrxBankPayNotifyMsg(retDTO.getPayTrxNo(), retDTO.getRequestBody());

            // 异步通知业务系统支付成功
            CompletableFuture.runAsync(() -> {
                PayOrderTrxPO queryCnt = new PayOrderTrxPO();
                queryCnt.setPayTrxNo(retDTO.getPayTrxNo());
                PayOrderTrxPO payOrderTrxEntity = payOrderTrxMapper.selectOne(queryCnt);

                sourceSysPayCallbackNotify(payOrderTrxEntity.getOrderNo());
            });
        }
        return retDTO.getRespBody();
    }

    /**
     * 退款成功异步共同处理
     *
     * @param sourceSys
     * @param payType
     * @param request
     */
    private String commonRefundCallbackNotify(String sourceSys, PayTypeEnum payType, HttpServletRequest request) {
        BankRefundCheckNotifyRetDTO retDTO = InsurUtil.getBankPayService(payType.getTypeCode()).bankCheckRefundNotify(sourceSys, request);
        if (retDTO.isRefundSuccess()) {
            PayRefundPO queryCnt = new PayRefundPO();
            queryCnt.setRefundTrxNo(retDTO.getRefundTrxNo());
            PayRefundPO payRefund = payRefundMapper.selectOne(queryCnt);
            insurPayService.syncPayRefundInfoWithBank(sourceSys, payRefund.getOrderNo(), payRefund.getPayType());
            payRefundMapper.updatePayRefundBankPayNotifyMsg(payRefund.getRefundTrxNo(), retDTO.getRequestBody());
        }
        return retDTO.getRespBody();
    }
}
